#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>

#include "sample_comm.h"
#include "plug_media_ao.h"

typedef struct tagMEDIA_AO_SOUNG_INFO
{
    PLUG_MEDIA_AO_SOUND_E enType;
    HI_CHAR azFilePath[64];
}MEDIA_AO_SOUNG_INFO;

typedef struct tagPLUG_MEDIA_AO_CONTEXT
{
    HI_BOOL bInit;
    HI_BOOL bSysSoundRet; /* 是否播放系统音频 */

    AO_CHN      AoChn;
    ADEC_CHN    AdChn;
    AUDIO_DEV AoDev;
    
    HI_S32 s32AoChnCnt;
    AIO_ATTR_S stAioAttr;
    pthread_mutex_t tAudioMutex;
    PLUG_MEDIA_AO_SOUND_E enType;

}PLUG_MEDIA_AO_CONTEXT;

static PLUG_MEDIA_AO_CONTEXT s_stAoCtx = 
{
    .bInit = HI_FALSE,
    .bSysSoundRet = HI_FALSE,
    .tAudioMutex = PTHREAD_MUTEX_INITIALIZER,
};

static const MEDIA_AO_SOUNG_INFO s_stSoundInfo[PLUG_MEDIA_AO_SOUND_BUTT] = 
{
    {PLUG_MEDIA_AO_SOUND_HEAD, PLUG_MEDIA_SOUND_PATH"/"}, 
    
#if(defined(HI_COMM_AI_ADAPT_DOOR))
    
    {PLUG_MEDIA_AO_SOUND_TEMPERATURE, PLUG_MEDIA_SOUND_PATH"/temperature.aac"}, 
    {PLUG_MEDIA_AO_SOUND_HUMIDITY, PLUG_MEDIA_SOUND_PATH"/humidity.aac"}, 
    {PLUG_MEDIA_AO_SOUND_SMOKE, PLUG_MEDIA_SOUND_PATH"/smoke.aac"}, 
    
#elif(defined(HI_COMM_AI_ADAPT_GESTURE))
#elif(defined(HI_COMM_AI_ADAPT_GUIDER))
    {PLUG_MEDIA_AO_SOUND_DISTANCE_AHEAD, PLUG_MEDIA_SOUND_PATH"/ahead.aac"}, 
    {PLUG_MEDIA_AO_SOUND_DISTANCE_BELOW, PLUG_MEDIA_SOUND_PATH"/below.aac"},
    {PLUG_MEDIA_AO_SOUND_DISTANCE_BELOW_PIT, PLUG_MEDIA_SOUND_PATH"/below_pit.aac"},
#endif
    {PLUG_MEDIA_AO_SOUND_PHOTO, PLUG_MEDIA_SOUND_PATH"/photo.aac"}, 
    {PLUG_MEDIA_AO_SOUND_START_RECODE, PLUG_MEDIA_SOUND_PATH"/start_recode.aac"}, 
    {PLUG_MEDIA_AO_SOUND_STOP_RECODE, PLUG_MEDIA_SOUND_PATH"/stop_recode.aac"}, 
    {PLUG_MEDIA_AO_SOUND_MUSIC, PLUG_MEDIA_SOUND_PATH"/music.aac"}, 
};

static HI_S32 getHisiVoicePakeSize(PAYLOAD_TYPE_E t, HI_S32 sample)
{
	switch(t)
	{
		case PT_G711A:
		case PT_G711U:
			return 320;
		case PT_G726:
			switch(sample)
			{
				case 16000:
					return 100;
				case 24000:
					return 155;
				case 32000:
					return 200;
				case 40000:
					return 250;
				default:
					return 250;
			}
			break;
		default:
			return 200;
	}
}

static HI_S32 addHisiVoiceHeader(HI_S16 *inputdata, HI_S16 *Hisivoicedata, HI_S32
	PersampleLen,HI_S32 inputsamplelen)
{
	int outlen = 0;
	short HisiHeader[2];
	short *copyHisidata, *copyinputdata;
	int copysamplelen = 0;
	HisiHeader[0] = (short)(0x001<<8) & (0x0300);
	HisiHeader[1] = PersampleLen & 0x00ff;
	copysamplelen = inputsamplelen;

	copyHisidata = Hisivoicedata;
	copyinputdata = inputdata;
	while(copysamplelen >= PersampleLen)
	{
		 memcpy(copyHisidata, HisiHeader, 2 * sizeof(short));
		 outlen += 2;
		 copyHisidata += 2;		 
		 memcpy(copyHisidata, copyinputdata, PersampleLen * sizeof(short));
		 
		 copyinputdata += PersampleLen;
		 copyHisidata += PersampleLen;
		 copysamplelen -= PersampleLen;
		 outlen += PersampleLen;
	}
	if(copysamplelen > 0)
	{
        memcpy(copyHisidata, HisiHeader, 2 * sizeof(short));
        outlen += 2;
        copyHisidata += 2;
        memcpy(copyHisidata, copyinputdata, copysamplelen * sizeof(short));
        outlen += copysamplelen;
	}
	return outlen;
}

static HI_CHAR* MEDIA_AO_GetFilePath(PLUG_MEDIA_AO_SOUND_E enType)
{
    HI_S32 s32Idx = 0;
    
    for (s32Idx = 0; s32Idx < PLUG_MEDIA_AO_SOUND_BUTT; s32Idx++)
    {
        if (enType == s_stSoundInfo[s32Idx].enType)
        {
            return s_stSoundInfo[s32Idx].azFilePath;
        }
    }
    
    return HI_NULL;
}

static HI_VOID *MEDIA_AO_Play(HI_VOID* arg)
{
    pthread_detach(pthread_self());
    
    HI_S32  s32Ret = HI_SUCCESS;
    AUDIO_STREAM_S stAudioStream;
    HI_U32 u32Len = 640;
    HI_U32 u32ReadLen;
    HI_U8* pu8AudioStream = NULL;
    HI_CHAR* pazSoundPath;
    FILE* pfd;
    PLUG_MEDIA_AO_SOUND_E enType = *(PLUG_MEDIA_AO_SOUND_E *)arg;
    
    if (HI_TRUE != s_stAoCtx.bSysSoundRet)
    {
        return HI_NULL;
    }

    pazSoundPath = MEDIA_AO_GetFilePath(enType);
    if ((pazSoundPath == HI_NULL) || (PLUG_MEDIA_AO_SOUND_HEAD == enType))
    {
        LOGE("MEDIA_AO_GetFilePath failed or path is null type:[%d]!\n", enType);
        return HI_NULL;
    }
    
    pthread_mutex_lock(&s_stAoCtx.tAudioMutex);
    pfd = fopen(pazSoundPath, "rb");
    if (NULL == pfd)
    {
        LOGE("open file failed!\n");
        pthread_mutex_unlock(&s_stAoCtx.tAudioMutex);
        return HI_NULL;
    }
    
    pu8AudioStream = (HI_U8*)malloc(sizeof(HI_U8) * MAX_AUDIO_STREAM_LEN);
    if (NULL == pu8AudioStream)
    {
        printf("%s: malloc failed!\n", __FUNCTION__);
        fclose(pfd);
        pfd = HI_NULL;
        pthread_mutex_unlock(&s_stAoCtx.tAudioMutex);
        return HI_NULL;
    }
    
    while (HI_TRUE == s_stAoCtx.bSysSoundRet)
    {
        /* read from file */
        stAudioStream.pStream = pu8AudioStream;
        u32ReadLen = fread(stAudioStream.pStream, 1, u32Len, pfd);
        if (u32ReadLen <= 0)
        {
            s32Ret = HI_MPI_ADEC_SendEndOfStream(s_stAoCtx.AdChn, HI_FALSE);
            if (HI_SUCCESS != s32Ret)
            {
                printf("%s: HI_MPI_ADEC_SendEndOfStream failed!\n", __FUNCTION__);
            }
            break;
        }

        /* here only demo adec streaming sending mode, but pack sending mode is commended */
        stAudioStream.u32Len = u32ReadLen;
        s32Ret = HI_MPI_ADEC_SendStream(s_stAoCtx.AdChn, &stAudioStream, HI_TRUE);
        if (HI_SUCCESS != s32Ret)
        {
            printf("%s: HI_MPI_ADEC_SendStream(%d) failed with %#x!\n", \
                   __FUNCTION__, s_stAoCtx.AdChn, s32Ret);
            break;
        }
    }

    free(pu8AudioStream);
    pu8AudioStream = HI_NULL;
    fclose(pfd);
    pfd = HI_NULL;
    
    pthread_mutex_unlock(&s_stAoCtx.tAudioMutex);
    
    return HI_NULL;
}

HI_S32 PLUG_MEDIA_AO_Init(PAYLOAD_TYPE_E enAdecType)
{
    HI_S32  s32Ret = HI_SUCCESS;
    s_stAoCtx.AdChn = AD_CHNN;
    s_stAoCtx.AoChn = AO_CHNN;
    s_stAoCtx.AoDev = AO_DEV;

    if(enAdecType == PT_AAC)
    {
        s_stAoCtx.stAioAttr.enSamplerate   = AUDIO_SAMPLE_RATE_48000;
        s_stAoCtx.stAioAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
        s_stAoCtx.stAioAttr.enWorkmode     = AIO_MODE_I2S_MASTER;
        s_stAoCtx.stAioAttr.enSoundmode    = AUDIO_SOUND_MODE_MONO;
        s_stAoCtx.stAioAttr.u32EXFlag      = 0;
        s_stAoCtx.stAioAttr.u32FrmNum      = 30;
        s_stAoCtx.stAioAttr.u32PtNumPerFrm = AACLC_SAMPLES_PER_FRAME;
        s_stAoCtx.stAioAttr.u32ChnCnt      = 1;
        s_stAoCtx.stAioAttr.u32ClkSel      = 1;
        s_stAoCtx.stAioAttr.enI2sType      = AIO_I2STYPE_INNERCODEC;
    }
    else if(enAdecType == PT_G711A)
    {
        s_stAoCtx.stAioAttr.enSamplerate   = AUDIO_SAMPLE_RATE_8000;
        s_stAoCtx.stAioAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
        s_stAoCtx.stAioAttr.enWorkmode     = AIO_MODE_I2S_MASTER;
        s_stAoCtx.stAioAttr.enSoundmode    = AUDIO_SOUND_MODE_MONO; 
        s_stAoCtx.stAioAttr.u32EXFlag      = 0;
        s_stAoCtx.stAioAttr.u32FrmNum      = 30;
        s_stAoCtx.stAioAttr.u32PtNumPerFrm = 1024;
        s_stAoCtx.stAioAttr.u32ChnCnt      = 1;
        s_stAoCtx.stAioAttr.u32ClkSel      = 0;
        s_stAoCtx.stAioAttr.enI2sType      = AIO_I2STYPE_INNERCODEC;
    }

    s32Ret = HI_MPI_ADEC_AacInit();
    HI_APPCOMM_CHECK_RETURN(s32Ret, s32Ret);

    s32Ret = SAMPLE_COMM_AUDIO_StartAdec(s_stAoCtx.AdChn, enAdecType);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StartAdec failed\n");
        goto ADECAO_ERR3;
    }
    
    s_stAoCtx.s32AoChnCnt = s_stAoCtx.stAioAttr.u32ChnCnt;

    s32Ret = SAMPLE_COMM_AUDIO_StartAo(s_stAoCtx.AoDev, s_stAoCtx.s32AoChnCnt,
                                 &s_stAoCtx.stAioAttr, AUDIO_SAMPLE_RATE_BUTT, HI_FALSE);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StartAo failed\n");
        goto ADECAO_ERR2;
    }
                                 
    s32Ret = SAMPLE_COMM_AUDIO_CfgAcodec(&s_stAoCtx.stAioAttr);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_CfgAcodec failed\n");
        goto ADECAO_ERR1;
    }
    
    s32Ret = SAMPLE_COMM_AUDIO_AoBindAdec(s_stAoCtx.AoDev, s_stAoCtx.AoChn, s_stAoCtx.AdChn);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_AoBindAdec failed\n");
        goto ADECAO_ERR1;
    }
    
    HI_MPI_AO_SetVolume(s_stAoCtx.AoDev, 3);
    
    s_stAoCtx.bInit = HI_TRUE;
    s_stAoCtx.bSysSoundRet = HI_TRUE;

    return HI_SUCCESS;

ADECAO_ERR0:
    s32Ret = SAMPLE_COMM_AUDIO_AoUnbindAdec(s_stAoCtx.AoDev, s_stAoCtx.AoChn, s_stAoCtx.AdChn);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_AoUnbindAdec failed![%d]\n", s32Ret);
    }

ADECAO_ERR1:
    s32Ret |= SAMPLE_COMM_AUDIO_StopAo(s_stAoCtx.AoDev, s_stAoCtx.s32AoChnCnt, HI_FALSE);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAo failed![%d]\n", s32Ret);
    }
ADECAO_ERR2:
    s32Ret |= SAMPLE_COMM_AUDIO_StopAdec(s_stAoCtx.AdChn);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAdec failed![%d]\n", s32Ret);
    }

ADECAO_ERR3:
    return s32Ret;
}

HI_S32 PLUG_MEDIA_AO_SystemSound(PLUG_MEDIA_AO_SOUND_E enType)
{
    if (s_stAoCtx.bSysSoundRet == HI_FALSE)
    {
        LOGI("PLUG_MEDIA_AO_SystemSound can not sound, maybe talk is use!\n");
    }
    
    HI_S32 s32Ret = HI_SUCCESS;
    pthread_t taskId;
    s_stAoCtx.enType = enType;

    s32Ret = pthread_create(&taskId, HI_NULL, MEDIA_AO_Play, (HI_VOID *)&(s_stAoCtx.enType));
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("pthread create PLUG_HISIGNALLING_MSRecv failed!\n");
    }
    
    return HI_SUCCESS;
}

HI_S32 PLUG_MEDIA_AO_TalkSound(PLUG_AO_STREAM_S* pTalkData)
{
    HI_S32 s32Ret = HI_SUCCESS;
    int outbufSize = 0;
    short *outbuf = HI_NULL;
    AUDIO_STREAM_S stAudioStream;
    memset(&stAudioStream, 0x0, sizeof(AUDIO_STREAM_S));

    HI_S32 packSize = getHisiVoicePakeSize(PT_G711A, 8000);
	if(pTalkData->u32Len < packSize)
	{
	    pTalkData->u32Len = packSize;
	}
	outbuf = malloc(((pTalkData->u32Len % packSize == 0) ? (pTalkData->u32Len / packSize + 1) * (packSize + 4) :
			(pTalkData->u32Len / packSize + 2) * (packSize + 4)) * sizeof(short));

	outbufSize = addHisiVoiceHeader((short*)pTalkData->pStream,(short*)outbuf, 
					packSize / 2, pTalkData->u32Len / 2) * 2;
					
	stAudioStream.pStream = (HI_U8 *)outbuf;
	stAudioStream.u32Len = outbufSize;
	stAudioStream.u64TimeStamp = pTalkData->u64TimeStamp;
	stAudioStream.u32Seq = 0;

    
    s32Ret = HI_MPI_ADEC_SendStream(s_stAoCtx.AdChn, &stAudioStream, HI_TRUE);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s: HI_MPI_ADEC_SendStream(%d) failed with %#x!\n", \
               __FUNCTION__, s_stAoCtx.AdChn, s32Ret);
        return HI_FAILURE;
    }
    return HI_SUCCESS;
}

HI_S32 PLUG_MEDIA_AO_SetSysSound(HI_BOOL bSysSoundRet)
{
    s_stAoCtx.bSysSoundRet = bSysSoundRet;
    
    return HI_SUCCESS;
}

HI_S32 PLUG_MEDIA_AO_Deinit(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    
    if (!s_stAoCtx.bInit)
    {
        LOGE("ao has deinit\n");
        return HI_SUCCESS;
    }

    s32Ret = SAMPLE_COMM_AUDIO_AoUnbindAdec(s_stAoCtx.AoDev, s_stAoCtx.AoChn, s_stAoCtx.AdChn);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_AoUnbindAdec failed![%d]\n", s32Ret);
    }

    s32Ret |= SAMPLE_COMM_AUDIO_StopAo(s_stAoCtx.AoDev, s_stAoCtx.s32AoChnCnt, HI_FALSE);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAo failed![%d]\n", s32Ret);
    }
    
    s32Ret |= SAMPLE_COMM_AUDIO_StopAdec(s_stAoCtx.AdChn);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAdec failed![%d]\n", s32Ret);
    }
    
    s32Ret = HI_MPI_ADEC_AacDeInit();
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("HI_MPI_ADEC_AacDeInit failed![%d]\n", s32Ret);
    }
    
    s_stAoCtx.bSysSoundRet = HI_FALSE;
    s_stAoCtx.bInit = HI_FALSE;
    
    return s32Ret;
}


